package org.tinaelle.servlet.utils.json;

/*
 * Copyright (c) 2002 JSON.org
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
 * Software.
 * 
 * The Software shall be used for Good, not Evil.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/**
 * This provides static methods to convert comma delimited text into a JSONArray, and to covert a JSONArray into comma
 * delimited text. Comma delimited text is a very popular format for data interchange. It is understood by most
 * database, spreadsheet, and organizer programs.
 * <p>
 * Each row of text represents a row in a table or a data record. Each row ends with a NEWLINE character. Each row
 * contains one or more values. Values are separated by commas. A value can contain any character except for comma,
 * unless is is wrapped in single quotes or double quotes.
 * <p>
 * The first row usually contains the names of the columns.
 * <p>
 * A comma delimited list can be converted into a JSONArray of JSONObjects. The names for the elements in the
 * JSONObjects can be taken from the names in the first row.
 * 
 * @author JSON.org
 * @version 2
 */
public class CDL {

	/**
	 * Get the next value. The value can be wrapped in quotes. The value can be empty.
	 * 
	 * @param x
	 *            A JSONTokener of the source text.
	 * @return The value string, or null if empty.
	 * @throws JSONException
	 *             if the quoted string is badly formed.
	 */
	private static String getValue(final JSONTokener x) throws JSONException {
		char c;
		do {
			c = x.next();
		} while ( ( c <= ' ' ) && ( c != 0 ) );
		switch ( c ) {
			case 0:
				return null;
			case '"':
			case '\'':
				return x.nextString( c );
			case ',':
				x.back();
				return "";
			default:
				x.back();
				return x.nextTo( ',' );
		}
	}

	/**
	 * Produce a JSONArray of strings from a row of comma delimited values.
	 * 
	 * @param x
	 *            A JSONTokener of the source text.
	 * @return A JSONArray of strings.
	 * @throws JSONException
	 */
	public static JSONArray rowToJSONArray(final JSONTokener x) throws JSONException {
		final JSONArray ja = new JSONArray();
		for ( ;; ) {
			final String value = getValue( x );
			if ( value == null ) {
				return null;
			}
			ja.put( value );
			for ( ;; ) {
				final char c = x.next();
				if ( c == ',' ) {
					break;
				}
				if ( c != ' ' ) {
					if ( ( c == '\n' ) || ( c == '\r' ) || ( c == 0 ) ) {
						return ja;
					}
					throw x.syntaxError( "Bad character '" + c + "' (" + ( int ) c + ")." );
				}
			}
		}
	}

	/**
	 * Produce a JSONObject from a row of comma delimited text, using a parallel JSONArray of strings to provides the
	 * names of the elements.
	 * 
	 * @param names
	 *            A JSONArray of names. This is commonly obtained from the first row of a comma delimited text file
	 *            using the rowToJSONArray method.
	 * @param x
	 *            A JSONTokener of the source text.
	 * @return A JSONObject combining the names and values.
	 * @throws JSONException
	 */
	public static JSONObject rowToJSONObject(final JSONArray names, final JSONTokener x) throws JSONException {
		final JSONArray ja = rowToJSONArray( x );
		return ja != null ? ja.toJSONObject( names ) : null;
	}

	/**
	 * Produce a JSONArray of JSONObjects from a comma delimited text string, using the first row as a source of names.
	 * 
	 * @param string
	 *            The comma delimited text.
	 * @return A JSONArray of JSONObjects.
	 * @throws JSONException
	 */
	public static JSONArray toJSONArray(final String string) throws JSONException {
		return toJSONArray( new JSONTokener( string ) );
	}

	/**
	 * Produce a JSONArray of JSONObjects from a comma delimited text string, using the first row as a source of names.
	 * 
	 * @param x
	 *            The JSONTokener containing the comma delimited text.
	 * @return A JSONArray of JSONObjects.
	 * @throws JSONException
	 */
	public static JSONArray toJSONArray(final JSONTokener x) throws JSONException {
		return toJSONArray( rowToJSONArray( x ), x );
	}

	/**
	 * Produce a JSONArray of JSONObjects from a comma delimited text string using a supplied JSONArray as the source of
	 * element names.
	 * 
	 * @param names
	 *            A JSONArray of strings.
	 * @param string
	 *            The comma delimited text.
	 * @return A JSONArray of JSONObjects.
	 * @throws JSONException
	 */
	public static JSONArray toJSONArray(final JSONArray names, final String string) throws JSONException {
		return toJSONArray( names, new JSONTokener( string ) );
	}

	/**
	 * Produce a JSONArray of JSONObjects from a comma delimited text string using a supplied JSONArray as the source of
	 * element names.
	 * 
	 * @param names
	 *            A JSONArray of strings.
	 * @param x
	 *            A JSONTokener of the source text.
	 * @return A JSONArray of JSONObjects.
	 * @throws JSONException
	 */
	public static JSONArray toJSONArray(final JSONArray names, final JSONTokener x) throws JSONException {
		if ( ( names == null ) || ( names.length() == 0 ) ) {
			return null;
		}
		final JSONArray ja = new JSONArray();
		for ( ;; ) {
			final JSONObject jo = rowToJSONObject( names, x );
			if ( jo == null ) {
				break;
			}
			ja.put( jo );
		}
		if ( ja.length() == 0 ) {
			return null;
		}
		return ja;
	}

	/**
	 * Produce a comma delimited text row from a JSONArray. Values containing the comma character will be quoted.
	 * 
	 * @param ja
	 *            A JSONArray of strings.
	 * @return A string ending in NEWLINE.
	 */
	public static String rowToString(final JSONArray ja) {
		final StringBuffer sb = new StringBuffer();
		for ( int i = 0; i < ja.length(); i += 1 ) {
			if ( i > 0 ) {
				sb.append( ',' );
			}
			final Object o = ja.opt( i );
			if ( o != null ) {
				final String s = o.toString();
				if ( s.indexOf( ',' ) >= 0 ) {
					if ( s.indexOf( '"' ) >= 0 ) {
						sb.append( '\'' );
						sb.append( s );
						sb.append( '\'' );
					} else {
						sb.append( '"' );
						sb.append( s );
						sb.append( '"' );
					}
				} else {
					sb.append( s );
				}
			}
		}
		sb.append( '\n' );
		return sb.toString();

	}

	/**
	 * Produce a comma delimited text from a JSONArray of JSONObjects. The first row will be a list of names obtained by
	 * inspecting the first JSONObject.
	 * 
	 * @param ja
	 *            A JSONArray of JSONObjects.
	 * @return A comma delimited text.
	 * @throws JSONException
	 */
	public static String toString(final JSONArray ja) throws JSONException {
		final JSONObject jo = ja.optJSONObject( 0 );
		if ( jo != null ) {
			final JSONArray names = jo.names();
			if ( names != null ) {
				return rowToString( names ) + toString( names, ja );
			}
		}
		return null;
	}

	/**
	 * Produce a comma delimited text from a JSONArray of JSONObjects using a provided list of names. The list of names
	 * is not included in the output.
	 * 
	 * @param names
	 *            A JSONArray of strings.
	 * @param ja
	 *            A JSONArray of JSONObjects.
	 * @return A comma delimited text.
	 * @throws JSONException
	 */
	public static String toString(final JSONArray names, final JSONArray ja) throws JSONException {
		if ( ( names == null ) || ( names.length() == 0 ) ) {
			return null;
		}
		final StringBuffer sb = new StringBuffer();
		for ( int i = 0; i < ja.length(); i += 1 ) {
			final JSONObject jo = ja.optJSONObject( i );
			if ( jo != null ) {
				sb.append( rowToString( jo.toJSONArray( names ) ) );
			}
		}
		return sb.toString();
	}
}
